/*
    XFlightRec
    Copyright (C) 2009 Mike Gehl

    X-Plane Plugin SDK
    Copyright (C) Sandy Barbour and Ben Supnik

    Data conversion routines to NAV3
    Copyright (C) 2000 Ted Wright
    NASA Business: wright@grc.nasa.gov, Remainder: wright@en.com

    This file is part of XFlightRec.

    XFlightRec is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    XFlightRec is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with XFlightRec.  If not, see <http://www.gnu.org/licenses/>.
    ---------------------------------------------------------------------------
    xflightrec.c
    Copyright (C) Mike Gehl 2009 <mjgehl@gmail.com>

    X-Plane plugin entry point. Init the plugin, create menus, callback
    functions, and write the flight record to a binary data file.

    XFlightRec is a flight recorder for X-Plane v9. Its purpose is to allow
    virtual pilots and virtual airlines to perfrom post-flight analysis.
    The format for the data file is the same as FLTREC.DLL for MS FS and
    can be read and evaluated by the NAV24.EXE application. See:
    http://www.avsim.com/hangar/utils/nav/

    The recorder when turned ON will create a file called "checkride.dat" in
    the X-Plane Resources directory. Every FLTREC_INTERVAL second(s), flight
    data (e.g. position, altitude, airspeed) will be appended to the binary
    data file.
*/

#include <stdio.h>
#include <string.h>

#if APL
#if defined(__MACH__)
#include <Carbon/Carbon.h>
#endif
#endif

#include "XPLMProcessing.h"
#include "XPLMDataAccess.h"
#include "XPLMUtilities.h"
#include "about.h"
#include "conversion.h"
#include "xflightrec.h"


/* Global Binary data file */
FILE *gOutputFile;

/* Global System path to data file */
char gOutputPath[255];

/* Global Status of recording function FLTREC_OFF,FLTREC_ON */
enum flight_recorder_status gRecStatus;

/* Global Structure for a single data record */
FLTREC_DATA gFltRec_data;

PLUGIN_API int XPluginStart(char *outName, char *outSig, char *outDesc)
{
	strcpy(outName, "XFlightRec");
	strcpy(outSig, "xplane.aav.fltrec");
	strcpy(outDesc, "A plugin that outputs flight recorder data to a binary date file.");

	#if APL && __MACH__
	char outputPath2[255];
	int result = 0;
	#endif

	/* Locate the X-System directory and then concatenate the file name. */
	XPLMGetSystemPath(gOutputPath);
	strcat(gOutputPath, "checkride.dat");

	#if APL && __MACH__
	result = ConvertPath(gOutputPath, outputPath2, sizeof(gOutputPath));
	if (result == 0)
		strcpy(gOutputPath, outputPath2);
	else
		XPLMDebugString("XFlightRec - Unable to convert path\n");
	#endif

	/* Data refs to record. */
	gZuluTime     = XPLMFindDataRef("sim/time/zulu_time_sec");
	gPlaneLat     = XPLMFindDataRef("sim/flightmodel/position/latitude");
	gPlaneLon     = XPLMFindDataRef("sim/flightmodel/position/longitude");
	gPlaneAlt     = XPLMFindDataRef("sim/flightmodel/position/elevation");
	gPlanePitch   = XPLMFindDataRef("sim/flightmodel/position/theta");
	gPlaneRoll    = XPLMFindDataRef("sim/flightmodel/position/phi");
	gPlaneHeading = XPLMFindDataRef("sim/flightmodel/position/psi");
	gPlaneIAS     = XPLMFindDataRef("sim/flightmodel/position/indicated_airspeed");
	gPlaneTAS     = XPLMFindDataRef("sim/flightmodel/position/true_airspeed");
	gPlaneGear    = XPLMFindDataRef("sim/flightmodel2/gear/deploy_ratio");

	/* Register the callback for once a second.  */
	XPLMRegisterFlightLoopCallback(
			MyFlightLoopCallback,	/* Callback */
			FLTREC_INTERVAL,		/* Interval */
			NULL);					/* refcon not used. */


	XPLMMenuID	myMenu;
    int			mySubMenuItem;

	/* First we put a new menu item into the plugin menu.
	 * This menu item will contain a submenu for us. */
	mySubMenuItem = XPLMAppendMenuItem(
						XPLMFindPluginsMenu(),	/* Put in plugins menu */
						"XFlightRec",	        /* Item Title */
						0,						/* Item Ref */
						1);						/* Force English */

	/* Now create a submenu attached to our menu item. */
	myMenu = XPLMCreateMenu(
						"fltrec",
						XPLMFindPluginsMenu(),
						mySubMenuItem, 			/* Menu Item to attach to. */
						MyMenuHandlerCallback,	/* The handler */
						0);						/* Handler Ref */

	/* Append menu items to submenu. */
	XPLMAppendMenuItem( myMenu,
						"Flight Recorder - ON",
						(void *) FLTREC_MENU_ON,
						1);
	XPLMAppendMenuItem( myMenu,
						"Flight Recorder - OFF",
						(void *) FLTREC_MENU_OFF,
						1);
    XPLMAppendMenuSeparator(myMenu);

    XPLMAppendMenuItem( myMenu,
						"About...",
						(void *) FLTREC_MENU_ABOUT,
						1);

    /* Init the Flight Record record and set the status to OFF */
    gOutputFile = NULL;
    gRecStatus = FLTREC_OFF;
    gFltRec_data.wVersion1 = 8;
    gFltRec_data.wVersion2 = 0;
    gFltRec_data.wVersion3 = 1;
    gFltRec_data.wVersion4 = 0;

    return 1;
}

PLUGIN_API void	XPluginStop(void)
{
	XPLMUnregisterFlightLoopCallback(MyFlightLoopCallback, NULL);
	fltrec_stop( );
}

PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

PLUGIN_API void XPluginDisable(void)
{
    /* Flush the file when we are disabled.  This is convenient; you
	 * can disable the plugin and then look at the output on disk. */
	if (gOutputFile)
        fflush(gOutputFile);
}

PLUGIN_API void XPluginReceiveMessage(
					XPLMPluginID	inFromWho,
					long			inMessage,
					void *			inParam)
{
}


/**********************************************************************/
void MyMenuHandlerCallback( void *inMenuRef, void *inItemRef)
{
    if ((long) inItemRef == FLTREC_MENU_ON)
        fltrec_start( );

    if ((long) inItemRef == FLTREC_MENU_OFF)
        fltrec_stop( );

    if ((long) inItemRef == FLTREC_MENU_ABOUT)
        fltrec_about_dialog( );
}

float MyFlightLoopCallback( float inElapsedSinceLastCall,
                            float inElapsedTimeSinceLastFlightLoop,
                            int inCounter,
                            void *inRefcon)
{
    if (gRecStatus == FLTREC_ON)
    {
        /* Convert the datarefs to FLTREC structure */
        to_time(XPLMGetDataf(gZuluTime), &gFltRec_data);
        to_lattitude(XPLMGetDataf(gPlaneLat), &gFltRec_data);
        to_longitude(XPLMGetDataf(gPlaneLon), &gFltRec_data);
        to_altitude(XPLMGetDataf(gPlaneAlt), &gFltRec_data);
        gFltRec_data.dwPitch = to_orientation(XPLMGetDataf(gPlanePitch));
        gFltRec_data.dwBank = to_orientation(XPLMGetDataf(gPlaneRoll));
        gFltRec_data.dwHeading = to_orientation(XPLMGetDataf(gPlaneHeading));
        gFltRec_data.dwTAS = to_airspeed(meters_per_sec_to_knots(XPLMGetDataf(gPlaneTAS)));
        gFltRec_data.dwIAS = to_airspeed(XPLMGetDataf(gPlaneIAS));

        /* TODO Fuel Percentages */

        float gear[3];
        XPLMGetDatavf(gPlaneGear, gear, 0, 3);
        gFltRec_data.wGear1 = to_gear_pos(gear[0]);
        gFltRec_data.wGear2 = to_gear_pos(gear[1]);
        gFltRec_data.wGear3 = to_gear_pos(gear[2]);

        /*  Write the data to a file. */
        if (gOutputFile)
        {
            fwrite(&gFltRec_data, sizeof(gFltRec_data), 1, gOutputFile);
        }
    }

	/*  Return Time interval that we want to be called again. */
	return FLTREC_INTERVAL;
}

void fltrec_stop( )
{
    gRecStatus = FLTREC_OFF;
    if (gOutputFile)
        fclose(gOutputFile);
    gOutputFile = NULL;
}

void fltrec_start( )
{
    gOutputFile = fopen(gOutputPath, "wb");
    gRecStatus = FLTREC_ON;
}

#if APL && __MACH__
#include <Carbon/Carbon.h>
int ConvertPath(const char * inPath, char * outPath, int outPathMaxLen)
{
	CFStringRef inStr = CFStringCreateWithCString(kCFAllocatorDefault, inPath ,kCFStringEncodingMacRoman);
	if (inStr == NULL)
		return -1;
	CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, inStr, kCFURLHFSPathStyle,0);
	CFStringRef outStr = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
	if (!CFStringGetCString(outStr, outPath, outPathMaxLen, kCFURLPOSIXPathStyle))
		return -1;
	CFRelease(outStr);
	CFRelease(url);
	CFRelease(inStr);
	return 0;
}
#endif
